// created on 10/12/2002 at 21:11
using System;
using System.Runtime.InteropServices;
using SoundEdge.Media.Wave;
using System.Collections.Generic;

namespace SoundEdge.Media.Mixer
{	
	/// <summary>
	/// Represents a mixer control
	/// </summary>
	public abstract class MixerControl 
	{
		internal MixerInterop.MIXERCONTROL mixerControl;
		internal MixerInterop.MIXERCONTROLDETAILS mixerControlDetails;
		/// <summary>
		/// Mixer Handle
		/// </summary>
        protected IntPtr mixerHandle;
		/// <summary>
		/// Number of Channels
		/// </summary>
		protected int nChannels;
        /// <summary>
        /// Mixer Handle Type
        /// </summary>
        protected MixerFlags mixerHandleType;

        /// <summary>
        /// Gets all the mixer controls
        /// </summary>
        /// <param name="mixerHandle"></param>
        /// <param name="mixerLine"></param>
        /// <param name="mixerHandleType"></param>
        /// <returns></returns>
        public static IList<MixerControl> GetMixerControls(IntPtr mixerHandle, MixerLine mixerLine, MixerFlags mixerHandleType)
        {
            List<MixerControl> controls = new List<MixerControl>();
            if (mixerLine.ControlsCount > 0)
            {
                int mixerControlSize = Marshal.SizeOf(typeof(MixerInterop.MIXERCONTROL));
                MixerInterop.MIXERLINECONTROLS mlc = new MixerInterop.MIXERLINECONTROLS();
                IntPtr pmc = Marshal.AllocHGlobal(mixerControlSize * mixerLine.ControlsCount);
                mlc.cbStruct = Marshal.SizeOf(mlc);
                mlc.dwLineID = mixerLine.LineId;
                mlc.cControls = mixerLine.ControlsCount;
                mlc.pamxctrl = pmc;
                mlc.cbmxctrl = Marshal.SizeOf(typeof(MixerInterop.MIXERCONTROL));

                MmResult err = MixerInterop.mixerGetLineControls(mixerHandle, ref mlc, MixerFlags.All | mixerHandleType);
                if (err != MmResult.NoError)
                {
                    Marshal.FreeHGlobal(pmc);
                    throw new MmException(err, "mixerGetLineControls");
                }
                for (int i = 0; i < mlc.cControls; i++)
                {
                    Int64 address = pmc.ToInt64() + mixerControlSize * i;

                    MixerInterop.MIXERCONTROL mc = (MixerInterop.MIXERCONTROL)Marshal.PtrToStructure((IntPtr)address, typeof(MixerInterop.MIXERCONTROL));
                    MixerControl mixerControl = MixerControl.GetMixerControl(mixerHandle, mixerLine.LineId, mc.dwControlID, mixerLine.Channels, mixerHandleType);

                    controls.Add(mixerControl);
                }
            }
            return controls;
        }

		/// <summary>
		/// Gets a specified Mixer Control
		/// </summary>
        /// <param name="mixerHandle">Mixer Handle</param>
		/// <param name="nLineID">Line ID</param>
		/// <param name="controlId">Control ID</param>
		/// <param name="nChannels">Number of Channels</param>
        /// <param name="mixerFlags">Flags to use (indicates the meaning of mixerHandle)</param>
		/// <returns></returns>
        public static MixerControl GetMixerControl(IntPtr mixerHandle, int nLineID, int controlId, int nChannels, MixerFlags mixerFlags) 
		{
			MixerInterop.MIXERLINECONTROLS mlc = new MixerInterop.MIXERLINECONTROLS();
			MixerInterop.MIXERCONTROL mc = new MixerInterop.MIXERCONTROL();
			
			// set up the pointer to a structure
			IntPtr pMixerControl = Marshal.AllocCoTaskMem(Marshal.SizeOf(mc));
			//Marshal.StructureToPtr(mc, pMixerControl, false);      
			
			mlc.cbStruct = Marshal.SizeOf(mlc);
			mlc.cControls = 1;
			mlc.dwControlID = controlId;
			mlc.cbmxctrl = Marshal.SizeOf(mc);
			mlc.pamxctrl = pMixerControl;
			mlc.dwLineID = nLineID;
            MmResult err = MixerInterop.mixerGetLineControls(mixerHandle, ref mlc, MixerFlags.OneById | mixerFlags);
			if(err != MmResult.NoError) {
				Marshal.FreeCoTaskMem(pMixerControl);
				throw new MmException(err,"mixerGetLineControls");
			}
			
			// retrieve the structure from the pointer
			mc = (MixerInterop.MIXERCONTROL) Marshal.PtrToStructure(mlc.pamxctrl, typeof(MixerInterop.MIXERCONTROL));
			Marshal.FreeCoTaskMem(pMixerControl);
			
			if(MixerControl.IsControlBoolean(mc.dwControlType)) 
			{
                return new BooleanMixerControl(mc, mixerHandle, mixerFlags, nChannels);
			}
			else if(MixerControl.IsControlSigned(mc.dwControlType)) 
			{
                return new SignedMixerControl(mc, mixerHandle, mixerFlags, nChannels);
			}
			else if(MixerControl.IsControlUnsigned(mc.dwControlType)) 
			{
                return new UnsignedMixerControl(mc, mixerHandle, mixerFlags, nChannels);
			}
			else if(MixerControl.IsControlListText(mc.dwControlType)) 
			{
                return new ListTextMixerControl(mc, mixerHandle, mixerFlags, nChannels);
			}
			else if(MixerControl.IsControlCustom(mc.dwControlType)) 
			{
                return new CustomMixerControl(mc, mixerHandle, mixerFlags, nChannels);
			}
			else 
			{
				throw new ApplicationException(String.Format("Unknown mixer control type {0}",mc.dwControlType));
			}					
		}
		
		/// <summary>
		/// Gets the control details
		/// </summary>
		protected void GetControlDetails() 
		{
			mixerControlDetails.cbStruct = Marshal.SizeOf(mixerControlDetails);
			mixerControlDetails.dwControlID = mixerControl.dwControlID;
			if(IsCustom) 
			{
				mixerControlDetails.cChannels = 0;
			}
			else if((mixerControl.fdwControl & MixerInterop.MIXERCONTROL_CONTROLF_UNIFORM) != 0) 
			{
				mixerControlDetails.cChannels = 1;
			}
			else 
			{
				mixerControlDetails.cChannels = nChannels;
			}
			
			
			if((mixerControl.fdwControl & MixerInterop.MIXERCONTROL_CONTROLF_MULTIPLE) != 0) 
			{
				mixerControlDetails.hwndOwner = (IntPtr)mixerControl.cMultipleItems;
			}
			else if(IsCustom) 
			{
				mixerControlDetails.hwndOwner = IntPtr.Zero; // TODO: special cases
			}
			else 
			{
                mixerControlDetails.hwndOwner = IntPtr.Zero;
			}
			
			if(IsBoolean) 
			{
				mixerControlDetails.cbDetails = Marshal.SizeOf(new MixerInterop.MIXERCONTROLDETAILS_BOOLEAN());
			}
			else if(IsListText) 
			{
				mixerControlDetails.cbDetails = Marshal.SizeOf(new MixerInterop.MIXERCONTROLDETAILS_LISTTEXT());
			}
			else if(IsSigned) 
			{
				mixerControlDetails.cbDetails = Marshal.SizeOf(new MixerInterop.MIXERCONTROLDETAILS_SIGNED());
			}
			else if(IsUnsigned) 
			{
				mixerControlDetails.cbDetails = Marshal.SizeOf(new MixerInterop.MIXERCONTROLDETAILS_UNSIGNED());
			}
			else 
			{ // must be custom
				mixerControlDetails.cbDetails = mixerControl.Metrics.customData;
			}
			// from the structs sample in MSDN (see MyPerson2)
			IntPtr buffer = Marshal.AllocCoTaskMem(mixerControlDetails.cbDetails * mixerControlDetails.cChannels);
      		// To copy stuff in:
      		// Marshal.StructureToPtr( theStruct, buffer, false );
			mixerControlDetails.paDetails = buffer;
            MmResult err = MixerInterop.mixerGetControlDetails(mixerHandle, ref mixerControlDetails, MixerFlags.Value | mixerHandleType);
			// let the derived classes get the details before we free the handle			
			if(err == MmResult.NoError) 
			{
				GetDetails(mixerControlDetails.paDetails);
			}
		    Marshal.FreeCoTaskMem(buffer);
			if(err != MmResult.NoError) 
			{
				throw new MmException(err,"mixerGetControlDetails");
			}
		}

		/// <summary>
		/// Gets the control details
		/// </summary>
		/// <param name="pDetails"></param>
		protected abstract void GetDetails(IntPtr pDetails);

		/// <summary>
		/// Mixer control name
		/// </summary>
		public String Name 
		{
			get 
			{
				return mixerControl.szName;
			}
		}
		
		/// <summary>
		/// Mixer control type
		/// </summary>
		public MixerControlType ControlType 
		{
			get 
			{
				return mixerControl.dwControlType;
			}
		}
		
		/// <summary>
		/// Returns true if this is a boolean control
		/// </summary>
		/// <param name="controlType">Control type</param>
		private static bool IsControlBoolean(MixerControlType controlType) 
		{
			switch(controlType) 
			{
			case MixerControlType.BooleanMeter:
			case MixerControlType.Boolean:
			case MixerControlType.Button:
			case MixerControlType.Loudness:
			case MixerControlType.Mono:
			case MixerControlType.Mute:
			case MixerControlType.OnOff:
			case MixerControlType.StereoEnhance:
			case MixerControlType.Mixer:
			case MixerControlType.MultipleSelect:
			case MixerControlType.Mux:
			case MixerControlType.SingleSelect:
				return true;
			default:
				return false;
			}
		}
		
		/// <summary>
		/// Is this a boolean control
		/// </summary>
		public bool IsBoolean 
		{
			get 
			{
				return MixerControl.IsControlBoolean(mixerControl.dwControlType);
			}
		}
		
		/// <summary>
		/// Determines whether a specified mixer control type is a list text control
		/// </summary>
		private static bool IsControlListText(MixerControlType controlType) 
		{
			switch(controlType) 
			{
			case MixerControlType.Equalizer:
			case MixerControlType.Mixer:
			case MixerControlType.MultipleSelect:
			case MixerControlType.Mux:
			case MixerControlType.SingleSelect:
				return true;
			default:
				return false;
			}
		}
		
		/// <summary>
		/// True if this is a list text control
		/// </summary>
		public bool IsListText 
		{
			get 
			{
				return MixerControl.IsControlListText(mixerControl.dwControlType);
			}
		}
		
		private static bool IsControlSigned(MixerControlType controlType) 
		{
			switch(controlType) 
			{
			case MixerControlType.PeakMeter:
			case MixerControlType.SignedMeter:
			case MixerControlType.Signed:
			case MixerControlType.Decibels:
			case MixerControlType.Pan:
			case MixerControlType.QSoundPan:
			case MixerControlType.Slider:
				return true;
			default:
				return false;
			}
		}
		
		/// <summary>
		/// True if this is a signed control
		/// </summary>
		public bool IsSigned 
		{
			get 
			{
				return MixerControl.IsControlSigned(mixerControl.dwControlType);
			}
		}
		
		private static bool IsControlUnsigned(MixerControlType controlType) 
		{
			switch(controlType) 
			{
			case MixerControlType.UnsignedMeter:
			case MixerControlType.Unsigned:
			case MixerControlType.Bass:
			case MixerControlType.Equalizer:
			case MixerControlType.Fader:
			case MixerControlType.Treble:
			case MixerControlType.Volume:
			case MixerControlType.MicroTime:
			case MixerControlType.MilliTime:
			case MixerControlType.Percent:
				return true;
			default:
				return false;
			}
		}

		/// <summary>
		/// True if this is an unsigned control
		/// </summary>
		public bool IsUnsigned 
		{
			get 
			{
				return MixerControl.IsControlUnsigned(mixerControl.dwControlType);
			}
		}

		private static bool IsControlCustom(MixerControlType controlType) 
		{
			return (controlType == MixerControlType.Custom);
		}
		
		/// <summary>
		/// True if this is a custom control
		/// </summary>
		public bool IsCustom 
		{
			get 
			{
				return MixerControl.IsControlCustom(mixerControl.dwControlType);
			}
		}

        /// <summary>
        /// String representation for debug purposes
        /// </summary>
        public override string ToString()
        {
            return String.Format("{0} {1}", Name, ControlType);
        }
	}
}
